#include "TcpServer.h"
#include "Logger.h"
#include "InetAddress.h"
#include "TcpConnection.h"


#include <functional>
#include <strings.h>

static EventLoop* CheckLoopNotNull(EventLoop* loop)
{
    if(loop == nullptr)
    {
        LOG_FATAL("%s:%s:%d mainloop is null! \n", __FILE__, __FUNCTION__, __LINE__);
    }
    return loop;
}    

TcpServer::TcpServer(EventLoop* loop, const InetAddress& listenAddr,const std::string& nameArg, Option option)
    : loop_(CheckLoopNotNull(loop))
    , ipPort_(listenAddr.toIpPort())
    , name_(nameArg)
    , acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
    , threadPool_(new EventLoopThreadPool(loop, name_))
    , connectionCallback_()
    , messageCallback_()
    , nextConnId_(1)
    , started_(0)
{
    //当有新用户连接时会执行TcpServer::newConnection回调
    acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection,this,
            std::placeholders::_1,std::placeholders::_2));
}

TcpServer::~TcpServer()
{
    for(auto& item : connections_)
    {
        // 这个局部的shared_ptr智能指针对象，出右括号，可以自动释放new出来的TcpConnection对象资源了
        TcpConnectionPtr conn(item.second);
        //在C++中，reset()函数通常与智能指针（如 std::unique_ptr, std::shared_ptr）一起使用。
        //这个函数用于改变智能指针的指向或释放智能指针当前管理的对象，并可选地接管新的原始指针。
        item.second.reset();

        // 销毁连接
        conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
    }
}

void TcpServer::setThreadNum(int numThreads) //设置底层subloop的个数
{
    threadPool_->setThreadNum(numThreads);
}

void TcpServer::start() //开启服务器监听
{
    if(started_ == 0) 
    {
        threadPool_->start(threadInitCallback_); //启动底层的loop线程池
        loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));
        started_++; //防止一个TcpServer对象被start多次
    }
}

//有一个新的客户连接，acceptor会执行这个回调操作
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
    //轮询算法，选择一个subloop来管理channel
    EventLoop* ioLoop = threadPool_->getNextLoop();
    char buf[64] = {0};
    snprintf(buf, sizeof(buf), "-%s#%d", ipPort_.c_str(), nextConnId_);
    ++nextConnId_;
    std::string connName = name_ + buf;  //定义连接名称

    LOG_INFO("TcpServer::newConnection [%s] - new connection [%s] from %s \n",
        name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());

    //通过sockfd获取其绑定的本机的ip地址和端口信息
    sockaddr_in local;
    ::bzero(&local,sizeof local);
    socklen_t addrlen = sizeof local;
    /**
     * getsockname函数：这是一个标准的socket API函数，用于获取与某个socket关联的本地协议地址。
     * sockfd：这是要查询的socket的文件描述符。它代表了一个打开的socket连接。
     *(sockaddr*)&local：这是一个指向sockaddr结构的指针，该结构用于接收与指定socket相关联的地址信息。
     *这里local是一个sockaddr结构体（或兼容的结构体，如sockaddr_in或sockaddr_in6），
     *它在调用前被分配，并在调用后填充了地址信息。通过强制类型转换(sockaddr*)，确保了函数接收正确的指针类型。
    */
    if(::getsockname(sockfd, (sockaddr*)&local, &addrlen) < 0)
    {
        LOG_ERROR("sockets::getLocalAddr");
    }

    InetAddress localAddr(local);

    //这边可以根据sockfd创建Socket和Channel对象
    TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
    connections_[connName] = conn;
    //下面的回调都是用户设置给TcpServer的，TcpServer传给TcpConnection,TcpConnection传给Channel
    conn->setConnecionCallback(connectionCallback_);
    conn->setMessageCallback(messageCallback_);
    conn->setWriteCompleteCallback(writeCompleteCallback_);

    //设置了如果关闭连接的回调
    conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
    //直接调用TcpConnection::connectEstablished
    ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
    loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
    LOG_INFO("TcpServer::removeConnectionInLoop [%s] - connection %s\n",name_.c_str(), conn->name().c_str());

    connections_.erase(conn->name());
    EventLoop* ioLoop = conn->getLoop();
    ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}